home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
b
/
b.lha
/
B
/
src
/
bint
/
b1fun.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-24
|
15KB
|
624 lines
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*
$Header: b1fun.c,v 1.4 85/08/22 16:48:24 timo Exp $
*/
/* Functions defined on numeric values. */
#include <errno.h> /* For EDOM and ERANGE */
#include "b.h"
#include "b0con.h"
#include "b1obj.h"
#include "b1num.h"
/*
* The visible routines here implement predefined B arithmetic operators,
* taking one or two numeric values as operands, and returning a numeric
* value.
* No type checking of operands is done: this must be done by the caller.
*/
typedef value (*valfun)();
typedef rational (*ratfun)();
typedef real (*appfun)();
typedef double (*mathfun)();
/*
* For the arithmetic functions (+, -, *, /) the same action is needed:
* 1) if both operands are Integral, use function from int_* submodule;
* 2) if both are Exact, use function from rat_* submodule (after possibly
* converting one of them from Integral to Rational);
* 3) otherwise, make both approximate and use function from app_*
* submodule.
* The functions performing the appropriate action for each of the submodules
* are passed as parameters.
* Division is a slight exception, since i/j can be a rational.
* See `quot' below.
*/
Hidden value dyop(u, v, int_fun, rat_fun, app_fun)
value u, v;
valfun int_fun;
ratfun rat_fun;
appfun app_fun;
{
if (Integral(u) && Integral(v)) /* Use integral operation */
return (*int_fun)(u, v);
if (Exact(u) && Exact(v)) {
rational u1, v1, a;
/* Use rational operation */
u1 = Integral(u) ? mk_rat((integer)u, int_1, 0) :
(rational) Copy(u);
v1 = Integral(v) ? mk_rat((integer)v, int_1, 0) :
(rational) Copy(v);
a = (*rat_fun)(u1, v1);
release((value) u1);
release((value) v1);
if (Denominator(a) == int_1 && Roundsize(a) == 0) {
integer b = (integer) Copy(Numerator(a));
release((value) a);
return (value) b;
}
return (value) a;
}
/* Use approximate operation */
{
real u1, v1, a;
u1 = Approximate(u) ? (real) Copy(u) : (real) approximate(u);
v1 = Approximate(v) ? (real) Copy(v) : (real) approximate(v);
a = (*app_fun)(u1, v1);
release((value) u1);
release((value) v1);
return (value) a;
}
}
Hidden integer isum(u, v) integer u, v; {
return int_sum(u, v);
}
Visible value sum(u, v) value u, v; {
if (IsSmallInt(u) && IsSmallInt(v))
return mk_integer(SmallIntVal(u) + SmallIntVal(v));
return dyop(u, v, (value (*)())isum, rat_sum, app_sum);
}
Hidden integer idiff(u, v) integer u, v; {
return int_diff(u, v);
}
Visible value diff(u, v) value u, v; {
if (IsSmallInt(u) && IsSmallInt(v))
return mk_integer(SmallIntVal(u) - SmallIntVal(v));
return dyop(u, v, (value (*)())idiff, rat_diff, app_diff);
}
Visible value prod(u, v) value u, v; {
if (IsSmallInt(u) && IsSmallInt(v))
return (value)
mk_int((double)SmallIntVal(u) * (double)SmallIntVal(v));
return dyop(u, v, (value (*)())int_prod, rat_prod, app_prod);
}
/*
* We cannot use int_quot (which performs integer division with truncation).
* Here is the routine we need.
*/
Hidden value xxx_quot(u, v) integer u, v; {
if (v == int_0) {
error(MESS(400, "in i/j, j is zero"));
return (value) Copy(u);
}
return mk_exact(u, v, 0);
}
Visible value quot(u, v) value u, v; {
return dyop(u, v, xxx_quot, rat_quot, app_quot);
}
/*
* Unary minus and abs follow the same principle but with only one operand.
*/
Visible value negated(u) value u; {
if (IsSmallInt(u)) return mk_integer(-SmallIntVal(u));
if (Integral(u))
return (value) int_neg((integer)u);
if (Rational(u))
return (value) rat_neg((rational)u);
return (value) app_neg((real)u);
}
Visible value absval(u) value u; {
if (Integral(u)) {
if (Msd((integer)u) < 0)
return (value) int_neg((integer)u);
} else if (Rational(u)) {
if (Msd(Numerator((rational)u)) < 0)
return (value) rat_neg((rational)u);
} else if (Approximate(u) && Frac((real)u) < 0)
return (value) app_neg((real)u);
return Copy(u);
}
/*
* The remaining operators follow less similar paths and some of
* them contain quite subtle code.
*/
Visible value mod(u, v) value u, v; {
value p, q, m, n;
if (v == (value)int_0 ||
Rational(v) && Numerator((rational)v) == int_0 ||
Approximate(v) && Frac((real)v) == 0) {
error(MESS(401, "in x mod y, y is zero"));
return Copy(u);
}
if (Integral(u) && Integral(v))
return (value) int_mod((integer)u, (integer)v);
/* Compute `u-v*floor(u/v)', as in the formal definition of `u mod v'. */
q = quot(u, v);
n = floorf(q);
release(q);
p = prod(n, v);
release(n);
m = diff(u, p);
release(p);
return m;
}
/*
* u**v has the most special cases of all the predefined arithmetic functions.
*/
Visible value power(u, v) value u, v; {
real ru, rv, rw;
if (Exact(u) && (Integral(v) ||
/* Next check catches for integers disguised as rationals: */
Rational(v) && Denominator((rational)v) == int_1)) {
rational a;
integer b = Integral(v) ? (integer)v : Numerator((rational)v);
/* Now b is really an integer. */
u = Integral(u) ? (value) mk_rat((integer)u, int_1, 0) :
Copy(u);
a = rat_power((rational)u, b);
release(u);
if (Denominator(a) == int_1) { /* Make integral result */
b = (integer) Copy(Numerator(a));
release((value) a);
return (value)b;
}
return (value)a;
}
if (Exact(v)) {
integer vn, vd;
int s;
ru = (real) approximate(u);
s = (Frac(ru) > 0) - (Frac(ru) < 0);
if (s < 0) rv = app_neg(ru), release((value)ru), ru = rv;
if (Integral(v)) {
vn = (integer)v;
vd = int_1;
} else {
vd = Denominator((rational)v);
if (s < 0 && Even(Lsd(vd)))
error(MESS(402, "in x**(p/q), x is negative and q is even"));
vn = Numerator((rational)v);
}
if (vn == int_0) {
release((value)ru);
return one;
}
if (s == 0 && Msd(vn) < 0) {
error(MESS(403, "in 0**y, y is negative"));
return (value) ru;
}
if (s < 0 && Even(Lsd(vn)))
s = 1;
rv = (real) approximate(v);
rw = app_power(ru, rv);
release((value)ru), release((value)rv);
if (s < 0) ru = app_neg(rw), release((value)rw), rw = ru;
return (value) rw;
}
/* Everything else: we now know u or v is approximate */
ru = (real) approximate(u);
if (Frac(ru) < 0) {
error(MESS(404, "in x**y, x is negative and y is not exact"));
return (value) ru;
}
rv = (real) approximate(v);
if (Frac(ru) == 0 && Frac(rv) < 0) {
error(MESS(405, "in 0**y, y is negative"));
release((value)rv);
return (value) ru;
}
rw = app_power(ru, rv);
release((value)ru), release((value)rv);
return (value) rw;
}
/*
* floor: for approximate numbers app_floor() is used;
* for integers it is a no-op; other exact numbers effectively calculate
* u - (u mod 1).
*/
Visible value floorf(u) value u; {
integer quo, rem, v;
digit div;
if (Integral(u)) return Copy(u);
if (Approximate(u)) return app_floor((real)u);
/* It is a rational number */
div = int_ldiv(Numerator((rational)u), Denominator((rational)u),
&quo, &rem);
if (div < 0 && rem != int_0) { /* Correction for negative noninteger */
v = int_diff(quo, int_1);
release((value) quo);
quo = v;
}
release((value) rem);
return (value) quo;
}
/*
* ceiling x is defined as -floor(-x);
* and that's how it's implemented, except for integers.
*/
Visible value ceilf(u) value u; {
value v;
if (Integral(u)) return Copy(u);
u = negated(u);
v = floorf(u);
release(u);
u = negated(v);
release(v);
return u;
}
/*
* round u is defined as floor(u+0.5), which is what is done here,
* except for integers which are left unchanged.
*/
Visible value round1(u) value u; {
value v, w;
if (Integral(u)) return Copy(u);
v = sum(u, (value)rat_half);
w = floorf(v);
release(v);
return w;
}
/*
* u round v is defined as 10**-u * round(v*10**u).
* A complication is that u round v is always printed with exactly u digits
* after the decimal point, even if this involves trailing zeros,
* or if v is an integer.
* Consequently, the result is always kept as a rational, even if it can be
* simplified to an integer, and the size field of the rational number
* (which is made negative to distinguish it from integers, and < -1 to
* distinguish it from approximate numbers) is used to store the number of
* significant digits.
* Thus a size of -2 means a normal rational number, and a size < -2
* means a rounded number to be printed with (-2 - length) digits
* after the decimal point. This last expression can be retrieved using
* the macro Roundsize(v) which should only be applied to Rational
* numbers.
*/
Visible value round2(u, v) value u, v; {
value w, tenp;
int i;
if (!Integral(u)) {
error(MESS(406, "in n round x, n is not an integer"));
i = 0;
} else
i = propintlet(intval(u));
tenp = tento(i);
v = prod(v, tenp);
w = round1(v);
release(v);
v = quot(w, tenp);
release(w);
release(tenp);
if (i > 0) { /* Set number of digits to be printed */
if (propintlet(-2 - i) < -2) {
if (Rational(v))
Length(v) = -2 - i;
else if (Integral(v)) {
w = v;
v = mk_exact((integer) w, int_1, i);
release(w);
}
}
}
return v;
}
/*
* sign u inspects the sign of either u, u's numerator or u's fractional part.
*/
Visible value signum(u) value u; {
int s;
if (Exact(u)) {
if (Rational(u))
u = (value) Numerator((rational)u);
s = u==(value)int_0 ? 0 : Msd((integer)u) < 0 ? -1 : 1;
} else
s = Frac((real)u) > 0 ? 1 : Frac((real)u) < 0 ? -1 : 0;
return MkSmallInt(s);
}
/*
* ~u makes an approximate number of any numerical value.
*/
Visible value approximate(u) value u; {
double x, e, expo;
register int i;
if (Approximate(u)) return Copy(u);
if (IsSmallInt(u))
x = SmallIntVal(u), expo = 0;
else if (Rational(u)) {
rational r = (rational) u;
integer p = Numerator(r), q;
double xp, xq;
int lp, lq;
if (p == int_0)
return Copy((value)app_0);
q = Denominator(r);
lp = IsSmallInt(p) ? 1 : Length(p);
lq = IsSmallInt(q) ? 1 : Length(q);
xp = 0, xq = 0;
expo = lp - lq;
if (IsSmallInt(p)) { xp = SmallIntVal(p); --expo; }
else {
for (i = Length(p)-1; i >= 0; --i) {
xp = xp*BASE + Digit(p, i);
--expo;
if (xp < -Maxreal/BASE || xp > Maxreal/BASE)
break;
}
}
if (IsSmallInt(q)) { xq = SmallIntVal(q); ++expo; }
else {
for (i = Length(q)-1; i >= 0; --i) {
xq = xq*BASE + Digit(q, i);
++expo;
if (xq > Maxreal/BASE)
break;
}
}
x = xp/xq;
}
else if (Integral(u)) {
integer p = (integer) u;
if (Length(p) == 0)
return Copy((value)app_0);
x = 0;
expo = Length(p);
for (i = Length(p)-1; i >= 0; --i) {
x = x*BASE + Digit(p, i);
--expo;
if (x < -Maxreal/BASE || x > Maxreal/BASE)
break;
}
}
while (expo < 0 && fabs(x) > BASE/Maxreal) ++expo, x /= BASE;
while (expo > 0 && fabs(x) < Maxreal/BASE) --expo, x *= BASE;
if (expo != 0) {
expo = expo*twologBASE + 1;
e = floor(expo);
x *= .5 * exp((expo-e) * logtwo);
}
else
e = 0;
return (value) mk_approx(x, e);
}
/*
* numerator v returns the numerator of v, whenever v is an exact number.
* For integers, that is v itself.
*/
Visible value numerator(v) value v; {
if (!Exact(v)) {
error(MESS(407, "*/ on approximate number"));
return zero;
}
if (Integral(v)) return Copy(v);
return (value) Copy(Numerator((rational)v));
}
/*
* /*v returns the denominator of v, whenever v is an exact number.
* For integers, that is 1.
*/
Visible value denominator(v) value v; {
if (!Exact(v)) {
error(MESS(408, "/* on approximate number"));
return zero;
}
if (Integral(v)) return one;
return (value) Copy(Denominator((rational)v));
}
/*
* u root v is defined as v**(1/u), where u is usually but need not be
* an integer.
*/
Visible value root2(u, v) value u, v; {
if (u == (value)int_0 ||
Rational(u) && Numerator((rational)u) == int_0 ||
Approximate(u) && Frac((real)u) == 0) {
error(MESS(409, "in n root x, n is zero"));
v = Copy(v);
} else {
u = quot((value)int_1, u);
v = power(v, u);
release(u);
}
return v;
}
/* root x is computed more exactly than n root x, by doing
one iteration step extra. This ~guarantees root(n**2) = n. */
Visible value root1(v) value v; {
value r = root2((value)int_2, v);
value v_over_r, theirsum, result;
if (Approximate(r) && Frac((real)r) == 0.0) return (value)r;
v_over_r = quot(v, r);
theirsum = sum(r, v_over_r), release(r), release(v_over_r);
result = quot(theirsum, (value)int_2), release(theirsum);
return result;
}
/* The rest of the mathematical functions */
Visible value pi() { return (value) mk_approx(3.141592653589793238463, 0.0); }
Visible value e() { return (value) mk_approx(2.718281828459045235360, 0.0); }
Hidden value trig(v, fun, zeroflag) value v; mathfun fun; bool zeroflag; {
real w = (real) approximate(v);
double expo = Expo(w), frac = Frac(w), x, result;
extern int errno;
if (expo <= Minexpo/2) {
if (zeroflag) return (value) w; /* sin small x = x, etc. */
frac = 0, expo = 0;
}
release((value)w);
if (expo > Maxexpo) errno = EDOM;
else {
x = ldexp(frac, (int)expo);
if (x >= Maxtrig || x <= -Maxtrig) errno = EDOM;
else {
errno = 0;
result = (*fun)(x);
}
}
if (errno != 0) {
if (errno == ERANGE)
error(MESS(410, "the result is too large to be representable"));
else if (errno == EDOM)
error(MESS(411, "x is too large to compute a meaningful result"));
else error(MESS(412, "math library error"));
return Copy((value)app_0);
}
return (value) mk_approx(result, 0.0);
}
Visible value sin1(v) value v; { return trig(v, sin, Yes); }
Visible value cos1(v) value v; { return trig(v, cos, No); }
Visible value tan1(v) value v; { return trig(v, tan, Yes); }
Visible value atn1(v) value v; {
real w = (real) approximate(v);
double expo = Expo(w), frac = Frac(w);
if (expo <= Minexpo + 2) return (value) w; /* atan of small x = x */
release((value)w);
if (expo > Maxexpo) expo = Maxexpo;
return (value) mk_approx(atan(ldexp(frac, (int)expo)), 0.0);
}
Visible value exp1(v) value v; {
real w = (real) approximate(v);
real x = app_exp(w);
release((value)w);
return (value) x;
}
Visible value log1(v) value v; {
real w = (real) approximate(v);
real x = app_log(w);
release((value)w);
return (value) x;
}
Visible value log2(u, v) value u, v;{
value w;
u = log1(u);
v = log1(v);
w = quot(v, u);
release(u), release(v);
return w;
}
Visible value atn2(u, v) value u, v; {
real ru = (real) approximate(u), rv = (real) approximate(v);
double uexpo = Expo(ru), ufrac = Frac(ru);
double vexpo = Expo(rv), vfrac = Frac(rv);
release((value)ru), release((value)rv);
if (uexpo > Maxexpo) uexpo = Maxexpo;
if (vexpo > Maxexpo) vexpo = Maxexpo;
return (value) mk_approx(
atan2(
vexpo < Minexpo ? 0.0 : ldexp(vfrac, (int)vexpo),
uexpo < Minexpo ? 0.0 : ldexp(ufrac, (int)uexpo)),
0.0);
}